Skip to content

stitch2d testing branch: hosted CI run (draft, do not merge) - #2

Merged
v-thampy merged 1158 commits into
mainfrom
stitch2d
Oct 22, 2026
Merged

v-thampy merged 1158 commits into
mainfrom
stitch2d

Conversation

@v-thampy

Copy link
Copy Markdown
Owner

Draft, not for merge. This PR exists only to run pr.yml's hosted jobs on the integrated testing branch stitch2d (0dfaa4e3).

The run of interest is windows-pixi-native, whose new step runs tests/core/test_windows_finite_publication.py on GitHub's Windows runner (local NTFS). It covers Average, Stitch and RSM finite publication without directory descriptors. It does not replace the maintainer's own Windows NTFS and SMB qualification.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L1UPY7Ep5ASRCWzUByxzNf

v-thampy and others added 30 commits October 13, 2026 09:12
…al stitch2d)

Composes the paused first implementation with Codex's ratified B1-B3
fixes before the kickoff's CR1-CR5 dispositions. No conflicts; no edit
beyond the merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
…ter review (test-only)

C1-C4 (headless): a live qualification waits for a missing row with a
typed SourceRevisionChanged and never borrows one; a complete line that
is not a row is refused with a ValueError naming it; published rows that
are gone or invalid are admitted without metadata so the published-
metadata fence reports them; a SPEC file truncated during the read leaves
the process alive. R1-R10 (live Runs): a row removed after readiness, a
malformed new row, a row rewritten before the reduction read; the SPEC
file removed, a covered row or header rewritten, and a raw rewrite with
its row while a frame waits; Stop inside the currency check and inside
JIT revalidation; progressive publication; a saved live output replays
and Appends unchanged inputs and refuses both after the SPEC file grows.
No production change; the frozen S, L and P1 files are not edited.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
…rrency, a bytes SPEC read

Codex's pre-writer review (CR1, CR2, CR6), on the composed cc5af3d:
- Live qualification and its re-checks hold each member to its own valid
  SPEC row (a complete line of the declared column count of numbers, read
  the same way by silx) and never fall back to the finite per-image
  reader. A published member whose row is gone or invalid makes every
  member come back without metadata, for the published-metadata fence; a
  new member whose row is not there yet is the existing typed retry; a
  complete line that is not a row at or before it is refused, naming the
  file, scan and row.
- The admitted row values stay in memory; a live Run's reduction read must
  consume exactly them, and a metadata file changing under that read is
  retried next poll instead of failing the Run.
- SPEC readiness exempts the frames the Run already published (B1's rule).
- When nothing beyond the published frames is ready while a frame waits,
  the attempt waits without admission while the published graph still
  validates exactly; Stop inside that check stops the Run; any other
  outcome is admitted as before (B1, B2 unchanged).
- The scan read copies the file once instead of mapping it, so a SPEC file
  truncated or rewritten during the read cannot fault the process.
Finite reads are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
…uments through (test-only)

test_dependency_graph_state_cannot_rebaseline_after_jit_freeze replaces
_directory_items with a double that copied its signature; the candidate's
new published_members keyword made it a TypeError in the terminal
battery. The double now forwards keyword arguments; its assertions are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
New tests/core/test_browse_reference_rings.py (H3a-c, H5a-d, H6, H6b, H7b, H9, H9b)
and tests/xdart/scattering/test_browse_reference_rings_run.py (H1, H1b, H2, H2b,
H4, H7, H8, G1-G11 and G3b): real Runs, the Run's own terminal handoff into
Browse, real reopen, and real saved outputs. Four superseded clauses amended
(Browse loading reason, the generic reason's text, terminal Browse phase rings,
terminal Browse grid per D4(c)); their Average/Reintegrate clauses unchanged.
New APIs are imported inside each row. No production change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
…en it does not exist yet (test-only)

The parent run showed G1[Int 1D], G4, G7 and G11 failing on
`view.raw_popup_dialog.isVisible()`: the dialog is created on first use, so the
helper raised AttributeError before reaching each row's behavioural assertion.
The helper now opens the popup when the dialog is absent or hidden. No
assertion, expected value or production file changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
…ng the raw popup (test-only)

Diagnosed from source before this second repair: ScientificView._open_raw_popup
returns while the popup button is disabled, and the button follows the selected
frame's detector availability, which a just-adopted Browse has not hydrated.
The helper now selects the frame, asserts the button is enabled, opens the popup
and selects again for its full raw read, as P3a's rows do. No assertion,
expected value or production file changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
… Int 2D's raw pane (test-only)

Browse has no Int 1D raw display: its raw popup reports "Full Raw is available only for an exact
acquisition frame", so Browse rings draw on Int 2D's raw pane. G1 keeps only its Int 2D case; G4 and
G7 run Int 2D; G11 drives the shape guard at the projection that attaches saved rings, since Browse
shows no full raw today. The dead raw-popup branch of the helper is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
…sentence (test-only)

Saved Int outputs now draw rings, so "drawn only on a Run's frames" stops being true; the row now
compares against RUN_RING_REASON and pins that the reworded text drops both stale claims.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
… output (test-only)

The older, slot-less output lives under test_data/eiger/xdart_processed_data, not beside the
slot-named outputs; the frozen path named a file that does not exist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
H7's directory helper wrote the PONI before creating its folder, and H8's "no recorded energy" copy
removed the monochromator wavelength but kept its energy, which records the same wavelength. Both
rows ran into an interface failure at the parent, so neither defect had been exercised.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
…leanup ends (test-only)

Browse refuses a new load while the previous context's cleanup is pending ("previous Browse cleanup
is pending", existing lifecycle policy), and G5 reopens while the old context's hydration is in
flight. The row now retries until the reopen is admitted, as a user's retry is, and keeps every
assertion: a new context, the old one released with its basis cleared, and new rings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
…ntegrate states (test-only)

The superseded row's kept clauses applied a synthetic projection at revision 1, which the page's shell
ignores as stale, so they only ever saw the grid state terminal Browse left. They now apply at the
page's own revision and check the Average and Reintegrate presentations themselves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
…rings

Terminal Browse and reopened Int outputs rebuild each frame's ring snapshot from the output's own
record: its accepted PONI values and detector configuration, its saved plan arguments through the
Run's plan route (execution_plan_shape, now shared by execution_plan_values), and its effective energy
through the Run's per-label rule (frame_ring_wavelengths, now shared by ScanSession). An older output
without an energy selection uses its recorded monochromator wavelength (D2); one without a detector
configuration refuses (D3); an energy scan draws only its last Append epoch (D5(a)).

The bounded reader (read_ring_record) reads only the declared local scalar nodes, at most 262,144
bytes each, once per Browse load inside its seals; BrowseContext carries the detached basis and clears
it on invalidation and release. attach_saved_rings puts a Browse Int frame's snapshot on its heavy
projection at projection time (thumbnail: the saved detector's shape; another full-raw shape refuses),
and ScientificView shows Browse loading and each refusal's own reason. In Browse the grid draws q
contours and withholds χ with a note (D4(c)). Average and Reintegrate outputs keep the generic reason.

ScientificView now forgets its shown phase request when it removes that request's drawings, so asking
for the same request again (a Browse frame re-selected through a transient state) recomputes it
instead of counting as unchanged (pre-existing; exposed once Browse frames carry rings).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7yaNDFtysn6idFk6L3AAu
Rows K1-K9 (tests/core/test_angular_support.py) and D1-D8
(tests/xdart/scattering/test_angular_support_strip.py) of the ratified kickoff, with
independent expectations: exact circle/rectangle arcs for perpendicular detectors, an
independent grazing-incidence rotation sampled around each ring (checked against pyFAI's
convention) and a sub-pixel raster union from pyFAI's pixel geometry for Stitch poses.
New APIs are imported inside each row so collection succeeds on the parent. The
plot-option bindings test gains the one value pair the new option needs (successor edit).
No production change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V77c36QooKtp2EjrGLimBn
…test-only oracle repair)

Parent polarity showed D1 and D8 failing at an import before their first behavioral
assertion; the kickoff requires D1, D2 and D8 to prove real missing behavior. Move those
imports below the first check (the missing Plot Options control after a real Run or
real Stitch). No expected value, tolerance or fixture changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V77c36QooKtp2EjrGLimBn
…ids (test-only oracle repair)

K1b and K4's energy check converted uniform 2θ edges into q, giving non-uniform q bins that
no reducer produces (pyFAI, MultiGeometry and XU grids are uniform in their native unit,
and the frozen profile stores first/last edges). Each unit now gets a uniform grid over the
same physical range, and K1b's "a q bin is a q average" check compares one physical bin
measured both ways against the independent model. No tolerance or expected-value rule
changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V77c36QooKtp2EjrGLimBn
…only oracle repair)

Running K8 against the first union implementation exposed oracle defects, none in the
production path:
- the source/plan helpers imported MetadataColumnSelector from the wrong module, selected
  two labels instead of every frame, and omitted what a native Stitch plan requires
  (native_records, a native artifact family, the 1-D χ window field azimuth_range_1d);
- the sub-pixel raster union was biased upward where support changes fast within a bin
  (bin q≈1.30: 185.55 at 0.18° cells, 183.93 at 0.09° and denser sampling, converging
  toward the exact 182.39). It is replaced by an exact independent model: rays around each
  ring intersected with each pose's detector plane (from pyFAI's forward geometry).
The tolerance (0.005 of the window) and every asserted behavior are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V77c36QooKtp2EjrGLimBn
…itch union (step 2)

xrd_tools.core.angular_support holds the shared values (profile, origin, reasons and
their text). xrd_tools.analysis.angular_support computes bin-mean support from each pose's
boundary: pixel edges between unmasked and masked/absent pixels, mapped by pyFAI's forward
geometry into (2θ, φ) and subdivided to ≤0.25° of φ; per-line crossing events with a seam
anchor give exact union intervals; χ_GI splits the region at q_horz = 0 (and the cut at the
beam), keeping the missing wedge empty. Windows follow each reducer's own rule (pyFAI's
normalize_azimuth_range after Int's offset, GI's fast/polar paths, MultiGeometry's scouted
extent, XU's closed centre-χ window); reachable directions a reducer drops refuse as
window_truncated.

Int: _reduce_frame captures the geometric mask actually applied (bound run mask, or
mask_with_detector's union; a mismatched detector mask stays unapplied) and the window and
float64 bins of its native profile into FrameReduction.angular_support_1d, through one
RunSupportCapture per Run (footprint cache, profile cache, a wavelength-independent line
table reused across an energy scan, the D3 geometry-key budget). Support never fails a
frame or changes intensity. run_reduction/ReductionSession take angular_support.

Stitch: MultiGeometry streams each frame's pose (operation mask with the detector's, never
value-invalid pixels) through the screen; only included frames join the union, repeated
poses count once. XU builds a corner-grid area in its own geometry (cch + 0.5, N + 1) and
unions XU footprints the same way. StitchOperationResult.angular_support carries the Run's
profile; run_stitch_operation takes angular_support.

Persistence, publication and GUI follow after the step-3 measured checkpoint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V77c36QooKtp2EjrGLimBn
… the support owner

stitch_2d_streaming now takes ``support`` like its 1-D and map-with-profile siblings; a map
alone has no native radial profile, so nothing is recorded. Found by
test_native_stitch_2d.py, whose kernel wrapper forwards the pass's keywords to the map-only
entry point.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V77c36QooKtp2EjrGLimBn
…el token

A kernel cancellation (SupportCancelled, now public) observed during a frame's support
capture stops that frame as the reduction's own cancellation, instead of being reported
as a support failure; the first frame's cold geometry is the long step on large detectors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V77c36QooKtp2EjrGLimBn
…ort kernel

Boundary extraction indexes only boundary pixels (np.nonzero per edge class) instead of
building np.indices over the detector, and the orientation check samples a sparse grid of
unmasked pixels instead of indexing every one. Measured on Eiger16M (step-3 M1 vs M1b):
the kernel's traced peak is 94 MiB for detector χ (215 MiB for χ_GI), retaining 25 MiB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V77c36QooKtp2EjrGLimBn
…measurements

Found by my own step-3 resource probes, before any review:
- An energy scan is one pose: the pose identity drops the wavelength (footprints are wavelength-independent).
  Before, a 651-frame energy scan computed 651 footprints.
- The line-table rebuild halved its spacing whenever the range alone fell short, so an increasing-wavelength
  scan doubled the table every frame (8073 -> 8280382 lines, 12 GB RSS after 12 frames). The spacing now halves
  only when a finer one is needed and the range grows by a quarter span on each side.
- A grazing-incidence incidence scan reuses its zero-tilt detector cut (q_horz does not depend on the incidence
  angle) and the loaded geometry, through a Run-owned SupportGeometryCache.
- Footprint geometry is built as pyFAI.geometry.Geometry with auto_gc off: pyFAI.load ran a full gc.collect()
  per pose, whose cost grows with the host heap. Boundary edges are extracted only on the detector-chi path.
- Workers sharing one Run compute a new footprint or line table once (the others wait, cancellably), and the
  footprint cache keeps at most 8 keys within its 64 MiB.

Regressions: tests/core/test_angular_support_capture.py (5 rows; against ef8ba20: energy rows 40 geometry keys,
concurrent row 4 footprint computations, GI rows missing SupportGeometryCache).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V77c36QooKtp2EjrGLimBn
v-thampy and others added 29 commits October 18, 2026 00:57
…nly)

Codex's terminal review of STITCH-BROWSE-ORDER-20261017 recorded two
non-blocking test-completion residuals:
- The gated Stitch and RSM rows now keep a real selection across the catalog
  landing and assert the current result, the selection, resident key/payload
  identity and the selected batch survive it; the no-selection batch follows
  the rows; a multi-result selection batches in Qt's selected order.
- The rows assert the catalog actually landed with the held results, and
  _held_catalog raises if a held read is never released instead of reading
  late.

Candidate: the three files 20 passed. Pre-fix product (77e1b9c src): the
gated rows still fail at the row-order assertion, after the landing check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01467C8Z5X76RUS1xk16SZBe
…he scan

Saving a Stitch calibration took 23-25 s on Windows for a 287-frame
calibrant scan (maintainer, 2026-10-21), almost all of it checking the
xrayutilities partner. Two things made it slow:

- every pixel was checked twice: a first full pass on the transfer's
  own XU model, then the gate through Stitch's own XU path. Candidates
  are now screened on the coarse detector (every 6th row, 23rd column)
  the fit already uses; the gate is the one check made on every pixel,
  and the asset and diagnostics record its |q|.
- every frame of the calibrant scan was checked. pair_validation_angles
  now takes the calibrant frames and a 7 x 7 grid over the range they,
  the scan's frames and any typed range span. Stitch admits that same
  range (it takes any frame inside it), so the recorded validated range
  is unchanged; both models are smooth in the angles.

Progress now moves on every frame of both stages (the screen was silent
for a formula arm and said only its fit seed otherwise; the gate moved
every 16th frame), and the bar gives the gate 90 % of its length.

Measured on this Mac, extended arm, 48 calibrant frames and a 287-frame
scan: 5.0 s -> 1.4 s (287 -> 93 frames); five-parameter arm 4.7 s ->
1.25 s. Recorded |dq| 2.3e-10 and 2.25e-9 A^-1, validated and control
ranges identical to the parent's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01467C8Z5X76RUS1xk16SZBe
Review of 2cccf80 (browser filename filter): 25 existing GUI tests failed
that pass at 5333c18, several on maintainer-visible behaviour:

- The filter hid the result being viewed. In Stitch a mode switch dropped
  the current result and cleared its display (a GI result shown in Stitch
  1D no longer stayed shown in Stitch 2D's layout); an RSM run whose
  Output was typed without `_rsm` showed its result but did not list it;
  in Int the shown scan's row and frames vanished while the plot kept it.
  The filter now narrows the other rows only: the current and selected
  results (Int: the selected scan and an overlay's artifacts) always stay
  listed. The Int cache key includes them.
- Stitch `_publish_results` is the parent's again: its extra redraw on
  any selection change drew a mounted result twice; with pinning the
  filter never drops a selection, so that branch had nothing to handle.
- Stitch 1D's default adds `_stitch2d`: Stitch 1D shows a Stitch 2D
  record's I(q), as Int 1D's default lists `_int2d`.
- Time order: a result not yet catalogued sorted last (timestamp 0) and
  jumped to the top when the catalog landed; it now sorts as the newest.
- `BrowserView.set_filename_filter(text)`: the filter as a person types it
  (mode defaults no longer replace it). Tests of browser mechanics on
  arbitrarily named fixtures opt out with it; page-listing fixtures use
  real `_rsm`/`_stitch2d` names; the Int test double gains the method.

Codex's filter tests follow the pinning rule. New rows: the viewed Int
scan and its frames stay listed through the filter; an uncatalogued
result lists as the newest in Time order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01467C8Z5X76RUS1xk16SZBe
RELEASE_NOTES 1.2.0 and the 1.1.5 -> 1.2.0 migration checklist now say
that each page lists its own results by default (clear the field for all,
including unsuffixed older or hand-named files; viewed results stay
listed; Time order in Stitch and RSM), and that a new Average writes
`_int1d_average`/`_int2d_average` beside a 1.1.5 `_average.nexus`, which
it no longer replaces (1.1.5 shipped `_average`). ARCHITECTURE's slot list
names the new Average slots. Three source comments used
`sample_int2d_average.nexus` as the forbidden chained name; it is now the
legitimate 2-D Average name, so they use the Reintegrate example. The
v1.1.5 section of MIGRATION keeps its historical `_average` names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01467C8Z5X76RUS1xk16SZBe
Residual of the browser filter review (5411f37 lists it: "A previously
pinned native row may linger until the next reconciliation"); the
maintainer chose "Fix both now", and 5411f37 already fixed the other
(Open no longer clears the filter).

In Stitch and RSM a result the filter kept only because it was viewed
stayed listed after a click moved the view to a result already in
memory, until the folder next changed (measured: still listed 4 s
later; gone once a file landed), and a no-selection batch, which uses
the listed rows, would have included it. Stitch and RSM rebuild their
list only when the folder, filter or mode changes; Int re-projects on
every change. A results list now re-applies the filter when its current
result or selection changes, deferred through a zero-delay Coalescer
owned by the view so the list is never rebuilt inside its own selection
signal, and only while a filter is in force.

Test: over both result-list profiles, a click on another listed result
drops the renamed row the filter kept while it was viewed; it fails at
5411f37 and passes here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01467C8Z5X76RUS1xk16SZBe
5411f37 stopped Open from clearing the browser filter, so RSM's default
`_rsm` filter now applies while these two read-order tests reopen seven
records named r0..r6.nexus: only the viewed record was listed, and the
row lookup for the next selection raised StopIteration
(test_a_failed_older_read_still_reads_the_newer_selection,
test_a_late_read_does_not_override_the_newer_selection; both pass at
c6c8a17, fail alone at 5411f37 and 179fd54). The fixtures now carry
RSM's own `_rsm` suffix, as real RSM outputs do; the read-order
assertions are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01467C8Z5X76RUS1xk16SZBe
@v-thampy
v-thampy merged commit 744cdca into main Oct 22, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant